#include "resourceloader.h"
#include <osgDB\Registry>
#include <osgDB\ReadFile>
#include <osgDB\WriteFile>
#include "utility.h"
#include <osg\LOD>
#include <osg\BlendFunc>

namespace CoreBase
{
	ResourceLoader* ResourceLoader::_resourceLoader = NULL;

	ResourceLoader::ResourceLoader()
	{}

	ResourceLoader::~ResourceLoader()
	{}

	ResourceLoader& ResourceLoader::GetInstance()
	{
		if(_resourceLoader == NULL)
			_resourceLoader = new ResourceLoader;
		return *_resourceLoader;
	}

	void ResourceLoader::flushObjectCache()
	{
		for(QMap<QString,osg::ref_ptr<osg::Node>>::iterator iter = _allLODNodeMap.begin();iter!=_allLODNodeMap.end();++iter)
		{
			iter.value()->releaseGLObjects();
		}
		_allLODNodeMap.clear();
		for(QMap<QString,osg::ref_ptr<osg::Node>>::iterator iter = _allNoCacheNodeMap.begin();iter!=_allNoCacheNodeMap.end();++iter)
		{
			iter.value()->releaseGLObjects();
		}
		_allNoCacheNodeMap.clear();
		_loader.flushObjectCache();
		_defaultAnimationPathControlMap.clear();
		_defaultFbxAnimationControlMap.clear();
	}

	ResourceLoader::Loadable::Loadable(void)
	{}

	ResourceLoader::Loadable::~Loadable(void)
	{}

	void ResourceLoader::Loadable::flushObjectCache()
	{
		osgDB::Registry::instance()->releaseGLObjects();
		osgDB::Registry::instance()->clearObjectCache();
	}

	osg::Node* ResourceLoader::Loadable::loadFile(const std::string& filename, bool useCache)
	{
		_filename = filename;
		osg::ref_ptr<osgDB::ReaderWriter::Options> options = new osgDB::ReaderWriter::Options;
		if (useCache)
		{
			options->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_ALL);
		}
		else
		{
			options->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_NONE);
		}
		osg::Node *model = osgDB::readNodeFile(_filename, options.get());
		if (model != 0)
		{
			model->setName(filename);
			return model;
		}
		else
		{
			return NULL;
		}
	}

	osg::Image* ResourceLoader::Loadable::loadImageFile( const std::string& filename)
	{
		osg::ref_ptr<osgDB::ReaderWriter::Options> options = new osgDB::ReaderWriter::Options;
		options->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_NONE);
		osg::Image *image =  osgDB::readImageFile(filename,options);
		return image;
	}

	osg::ref_ptr<osg::Node> ResourceLoader::getNodeByName(const QString& name,bool usecache,bool useosgcache)
	{
		if(usecache && _allLODNodeMap.contains(name))
		{
			return _allLODNodeMap.value(name);
		}
		osg::Node* node_1 = NULL;
		osg::Node* node_2 = NULL;
		osg::Node* node_3 = NULL;
		osg::Node* lodnode = NULL;
		node_1 = _loader.loadFile(ChineseTexttoStdstring(name), useosgcache);
		QString meshfilelod = name;
		meshfilelod.replace("_1","_2");
		if(meshfilelod!=name)
			node_2 = _loader.loadFile(ChineseTexttoStdstring(meshfilelod), useosgcache);
		meshfilelod = name;
		meshfilelod.replace("_1","_3");
		if(meshfilelod!=name)
			node_3 = _loader.loadFile(ChineseTexttoStdstring(meshfilelod), useosgcache);
		if(node_1 == NULL&&node_2 == NULL&&node_3 == NULL)
			return NULL;
		if(node_1 != NULL&&node_2 != NULL || node_3 != NULL&&node_2 != NULL || node_1 != NULL&&node_3 != NULL)
		{
			lodnode = new osg::LOD;
			if(node_1 == NULL)
				node_1 = node_2;
			if(node_2 == NULL)
				node_2 = node_3;
			if(node_3 == NULL)
				node_3 = node_2;
			node_1->setStateSet(NULL);
			node_2->setStateSet(NULL);
			node_3->setStateSet(NULL);
			dynamic_cast<osg::LOD*>(lodnode)->addChild(node_1,0,100);
			dynamic_cast<osg::LOD*>(lodnode)->addChild(node_2,100,1000);
			dynamic_cast<osg::LOD*>(lodnode)->addChild(node_3,1000,99999);
		}
		else
		{
			lodnode = node_1 == NULL?node_2:node_1;
			lodnode = lodnode == NULL?node_3:lodnode;
		}
		//control  default animation
		if(!_testAnimationPathCallbackControl.valid())
			_testAnimationPathCallbackControl = new AnimationPathCallbackControl;
		if(_testAnimationPathCallbackControl->haveAnimationPathCallback(lodnode))
		{
			_testAnimationPathCallbackControl->setupNode(lodnode);
			_testAnimationPathCallbackControl->stopAnimation(0);
			_defaultAnimationPathControlMap.insertMulti(name,_testAnimationPathCallbackControl);
			_testAnimationPathCallbackControl = NULL;
		}
		if(!_testFbxAnimationControl.valid())
			_testFbxAnimationControl = new FbxAnimationControl;
		if(_testFbxAnimationControl->havefbxAnimationCallback(lodnode))
		{
			_testFbxAnimationControl->registerAnimationCallback(name,lodnode);
			_testFbxAnimationControl->stop(name);
			_defaultFbxAnimationControlMap.insertMulti(name,_testFbxAnimationControl);
			_testFbxAnimationControl = NULL;
		}
		if(usecache)
		{
			_allLODNodeMap.insert(name,lodnode);
		}
		else
		{
			_allNoCacheNodeMap.insertMulti(name,lodnode);
		}
		return lodnode;
	}

	osg::ref_ptr<AnimationPathCallbackControl> ResourceLoader::getAnimationPathCallbackControlCache(const QString& name)
	{
		if(_defaultAnimationPathControlMap.contains(name))
		{
			return _defaultAnimationPathControlMap[name];
		}
		osg::ref_ptr<osg::Node> node = CoreBase::ResourceLoader::GetInstance().getNodeByName(name,false);
		if(_defaultAnimationPathControlMap.contains(name))
		{
			return _defaultAnimationPathControlMap[name];
		}
		return NULL;
	}

	void ResourceLoader::removeAnimationPathCallbackControlFromCache(const QString& name)
	{
		if(_allLODNodeMap.contains(name))
		{
			_allLODNodeMap.remove(name);
		}
		if(_allNoCacheNodeMap.contains(name))
		{
			_allNoCacheNodeMap.remove(name);
		}
		if(_defaultAnimationPathControlMap.contains(name))
			_defaultAnimationPathControlMap.remove(name);
	}

	void ResourceLoader::removeNodeFromCache(const QString& name)
	{
		for(QMap<QString,osg::ref_ptr<osg::Node>>::iterator iter = _removeNodeMap.begin();iter!=_removeNodeMap.end();++iter)
		{
			iter.value()->releaseGLObjects();
		}
		_removeNodeMap.clear();
		if(_allLODNodeMap.contains(name))
		{
			_removeNodeMap.insertMulti(name,_allLODNodeMap[name]);
			_allLODNodeMap.remove(name);
		}
		if(_allNoCacheNodeMap.contains(name))
		{
			_allNoCacheNodeMap.remove(name);
		}
	}

	osg::ref_ptr<osg::Node> ResourceLoader::getEnvironmentNodeByName(const QString& name,int distance)
	{
		if(_allLODNodeMap.contains(name))
		{
			return _allLODNodeMap.value(name);
		}
		osg::Group* node_1 = NULL;
		osg::Node* lodnode = NULL;
		osg::Node* nodeleaf = _loader.loadFile(ChineseTexttoStdstring(name), false);
		if(nodeleaf == NULL)
			return NULL;
		lodnode = new osg::LOD;
		if(!_environmentStateSet.valid())
		{
			_environmentStateSet = new osg::StateSet;
			_environmentStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
			_environmentStateSet->setMode(GL_BLEND,osg::StateAttribute::ON|osg::StateAttribute::PROTECTED);
		//	//_environmentStateSet->setMode(GL_DEPTH_TEST,osg::StateAttribute::ON);
		//	//_environmentStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
		//	//_environmentStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
		}
		nodeleaf->setStateSet(_environmentStateSet);
		//nodetruck->setStateSet(NULL);
		node_1 = new osg::Group;
		node_1->addChild(nodeleaf);
		dynamic_cast<osg::LOD*>(lodnode)->addChild(node_1,0,distance);
		dynamic_cast<osg::LOD*>(lodnode)->addChild(NULL,distance,999999);
		_allLODNodeMap.insert(name,lodnode);
		return lodnode;
	}

	osg::ref_ptr<osg::Image> ResourceLoader::getImageByFileName(const QString& name)
	{
		if(_filenameToImageCache.contains(name))
			return _filenameToImageCache.value(name);
		std::string filename = ChineseTexttoStdstring(name);
		osg::ref_ptr<osg::Image> image = _loader.loadImageFile(filename);
		if(image.valid())
		{
			image->setFileName(ChineseTexttoStdstring(name));
			_filenameToImageCache.insert(name,image);
			return image;
		}
		for(int i=0;i<_defaultSourceDirectory.size();++i)
		{
			filename = ChineseTexttoStdstring(_defaultSourceDirectory.at(i)+name);
			osg::ref_ptr<osg::Image> image = _loader.loadImageFile(filename);
			if(image.valid())
			{
				image->setFileName(filename);
				_filenameToImageCache.insert(name,image);
				return image;
			}
		}
		return NULL;
	}

	osg::ref_ptr<osg::Texture2D> ResourceLoader::getTextureByFileName(const QString& name,unsigned int index)
	{
		if(_filenameToTexture2DCache.contains(name))
			return _filenameToTexture2DCache.value(name);
		osg::ref_ptr<osg::Image> image = getImageByFileName(name);
		if(image.valid())
		{
			osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;
			tex->setImage(index,image);
			_filenameToTexture2DCache.insert(name,tex);
			return tex.get();
		}
		return NULL;
	}

	void ResourceLoader::setDefaultSourceDirectory(QVector<QString> vec)
	{
		_defaultSourceDirectory = vec;
	}

	void ResourceLoader::saveIVENode(osg::Node* node,const QString& filename)
	{
		//supportsExtension("ive","OpenSceneGraph native binary format");
		//supportsOption("compressed","Export option, use zlib compression to compress the data in the .ive ");
		//supportsOption("noTexturesInIVEFile","Export option");
		//supportsOption("includeImageFileInIVEFile","Export option");
		//supportsOption("compressImageData","Export option");
		//supportsOption("inlineExternalReferencesInIVEFile","Export option");
		//supportsOption("noWriteExternalReferenceFiles","Export option");
		//supportsOption("useOriginalExternalReferences","Export option");
		//supportsOption("TerrainMaximumErrorToSizeRatio=value","Export option that controls error matric used to determine terrain HeightField storage precision.");
		//supportsOption("noLoadExternalReferenceFiles","Import option");
		//supportsOption("OutputTextureFiles","Write out the texture images to file");
		osg::ref_ptr<osgDB::ReaderWriter::Options>  op = new osgDB::ReaderWriter::Options();
		//op->setOptionString("compressed");
		op->setOptionString("includeImageFileInIVEFile");
		//op->setOptionString("compressImageData");
		bool result = osgDB::writeNodeFile(*node,ChineseTexttoStdstring(filename),op.get());
	}

	void ResourceLoader::saveOSGNode(osg::Node* node,const QString& filename)
	{
		bool result = osgDB::writeNodeFile(*node,ChineseTexttoStdstring(filename));
	}

	void ResourceLoader::saveImage(osg::Image* image,const std::string& filename)
	{
		osgDB::writeImageFile(*image,filename) ;
	}
}